今天我們來創建其他的頁面。任務新增頁、圖表頁、設定頁、任務詳情頁。這裡會使用一些套件來進行修改調整。
安裝'react-native-safe-area-context
、@wuba/react-native-echarts echarts @shopify/react-native-skia react-native-svg
修改一下我們目前的App.tsx:
import React, { useState } from 'react';
import { View, Text, TouchableOpacity, StyleSheet, StatusBar, useColorScheme } from 'react-native';
import { SafeAreaProvider, SafeAreaView } from 'react-native-safe-area-context';
import { Colors } from 'react-native/Libraries/NewAppScreen';
import HomePage from './src/pages/home.page';
function MainContent() {
const isDarkMode = useColorScheme() === 'dark';
const [currentPage, setCurrentPage] = useState('home');
const backgroundStyle = {
backgroundColor: isDarkMode ? Colors.darker : Colors.lighter,
flex: 1,
};
const renderPage = () => {
switch (currentPage) {
case 'home':
return <HomePage isDarkMode={isDarkMode} />;
case 'add':
return <Text>新增任務頁面</Text>;
case 'stats':
return <Text>統計頁面</Text>;
case 'settings':
return <Text>設置頁面</Text>;
default:
return <HomePage isDarkMode={isDarkMode} />;
}
};
return (
<SafeAreaView style={backgroundStyle}>
<StatusBar
barStyle={isDarkMode ? 'light-content' : 'dark-content'}
backgroundColor={backgroundStyle.backgroundColor}
/>
<View style={styles.topArea}>
<Text style={styles.topText}>我的應用</Text>
</View>
<View style={styles.container}>
{renderPage()}
</View>
<View style={styles.bottomNav}>
<TouchableOpacity style={styles.navItem} onPress={() => setCurrentPage('home')}>
<Text style={styles.navText}>主頁</Text>
</TouchableOpacity>
<TouchableOpacity style={styles.navItem} onPress={() => setCurrentPage('add')}>
<Text style={styles.navText}>新增</Text>
</TouchableOpacity>
<TouchableOpacity style={styles.navItem} onPress={() => setCurrentPage('stats')}>
<Text style={styles.navText}>統計</Text>
</TouchableOpacity>
<TouchableOpacity style={styles.navItem} onPress={() => setCurrentPage('settings')}>
<Text style={styles.navText}>設置</Text>
</TouchableOpacity>
</View>
</SafeAreaView>
);
}
function App(): React.JSX.Element {
return (
<SafeAreaProvider>
<MainContent />
</SafeAreaProvider>
);
}
const styles = StyleSheet.create({
container: {
flex: 1,
},
topArea: {
height: 50,
justifyContent: 'center',
alignItems: 'center',
borderBottomWidth: 1,
borderBottomColor: '#ccc',
backgroundColor: '#f8f8f8',
},
topText: {
fontSize: 18,
fontWeight: 'bold',
},
bottomNav: {
flexDirection: 'row',
justifyContent: 'space-around',
alignItems: 'center',
height: 50,
borderTopWidth: 1,
borderTopColor: '#ccc',
backgroundColor: '#f8f8f8',
},
navItem: {
flex: 1,
alignItems: 'center',
justifyContent: 'center',
height: '100%',
},
navText: {
fontSize: 12,
},
});
export default App;
src\pages\home.page.tsx
:
import React, { PropsWithChildren } from 'react';
import { View, Text, TextInput, FlatList, StyleSheet } from 'react-native';
import { Colors } from 'react-native/Libraries/NewAppScreen';
const styles = StyleSheet.create({
container: {
flex: 1,
padding: 10,
},
title: {
fontSize: 18,
fontWeight: 'bold',
marginBottom: 10,
},
searchBar: {
height: 40,
borderColor: 'gray',
borderWidth: 1,
borderRadius: 5,
paddingHorizontal: 10,
marginBottom: 10,
},
taskList: {
flex: 1,
},
taskItem: {
padding: 10,
borderBottomWidth: 1,
borderBottomColor: '#ccc',
},
});
type Task = {
id: string;
title: string;
}
type HomePageProps = PropsWithChildren<{
isDarkMode?: boolean;
}>;
const HomePage = ({children, isDarkMode = false}: HomePageProps) => {
const tasks: Task[] = [
{ id: '1', title: 'Task 1' },
{ id: '2', title: 'Task 2' },
{ id: '3', title: 'Task 3' },
];
const renderTask = ({ item }: { item: Task }) => (
<View style={styles.taskItem}>
<Text>{item.title}</Text>
</View>
);
return (
<View style={styles.container}>
<Text style={styles.title}>主畫面 (任務列表)</Text>
<TextInput
style={styles.searchBar}
placeholder="搜索欄"
placeholderTextColor={isDarkMode ? Colors.light : Colors.dark}
/>
<FlatList
style={styles.taskList}
data={tasks}
renderItem={renderTask}
keyExtractor={item => item.id}
/>
</View>
);
};
export default HomePage;
src\pages\add-task.page.tsx
:
import React, { useState } from 'react';
import { Text, TextInput, TouchableOpacity, StyleSheet, ScrollView } from 'react-native';
type AddTaskPageProps = {
isDarkMode: boolean;
};
const AddTaskPage: React.FC<AddTaskPageProps> = ({ isDarkMode }) => {
const [title, setTitle] = useState('');
const [description, setDescription] = useState('');
const [dueDate, setDueDate] = useState(new Date());
const [priority, setPriority] = useState('');
const [reminder, setReminder] = useState('');
const [tags, setTags] = useState('');
const handleSubmit = () => {
// Handle task submission logic here
console.log({ title, description, dueDate, priority, reminder, tags });
};
return (
<ScrollView style={styles.container}>
<Text style={styles.title}>新增任務畫面</Text>
<TextInput
style={styles.input}
placeholder="任務標題"
value={title}
onChangeText={setTitle}
/>
<TextInput
style={[styles.input, styles.multilineInput]}
placeholder="任務描述"
multiline
numberOfLines={3}
value={description}
onChangeText={setDescription}
/>
<Text style={styles.label}>到期日期</Text>
<TextInput
style={styles.input}
placeholder="優先級"
value={priority}
onChangeText={setPriority}
/>
<TextInput
style={styles.input}
placeholder="提醒設置"
value={reminder}
onChangeText={setReminder}
/>
<TextInput
style={styles.input}
placeholder="標籤"
value={tags}
onChangeText={setTags}
/>
<TouchableOpacity style={styles.submitButton} onPress={handleSubmit}>
<Text style={styles.submitButtonText}>新增任務</Text>
</TouchableOpacity>
</ScrollView>
);
};
const styles = StyleSheet.create({
container: {
flex: 1,
padding: 10,
},
title: {
fontSize: 18,
fontWeight: 'bold',
marginBottom: 10,
},
input: {
height: 40,
borderColor: 'gray',
borderWidth: 1,
borderRadius: 5,
paddingHorizontal: 10,
marginBottom: 10,
},
multilineInput: {
height: 80,
},
label: {
fontSize: 16,
marginBottom: 5,
},
submitButton: {
backgroundColor: '#007AFF',
padding: 10,
borderRadius: 5,
alignItems: 'center',
marginTop: 10,
},
submitButtonText: {
color: 'white',
fontSize: 16,
fontWeight: 'bold',
},
});
export default AddTaskPage;
src\pages\task-detail.page.tsx
:
import React, { useState } from 'react';
import { View, Text, StyleSheet, TouchableOpacity, Switch } from 'react-native';
type TaskDetailsPageProps = {
isDarkMode: boolean;
task: {
id: string;
title: string;
description: string;
dueDate: Date;
priority: string;
isCompleted: boolean;
};
};
const TaskDetailsPage: React.FC<TaskDetailsPageProps> = ({ isDarkMode, task }) => {
const [isCompleted, setIsCompleted] = useState(task.isCompleted);
const handleEdit = () => {
// Handle edit logic
console.log('Edit task:', task.id);
};
const handleDelete = () => {
// Handle delete logic
console.log('Delete task:', task.id);
};
const toggleCompletion = () => {
setIsCompleted(!isCompleted);
// Update task completion status in the backend
console.log('Task completion toggled:', task.id, !isCompleted);
};
return (
<View style={styles.container}>
<Text style={styles.title}>{task.title}</Text>
<Text style={styles.description}>{task.description}</Text>
<Text style={styles.info}>到期日期: {task.dueDate.toLocaleDateString()}</Text>
<Text style={styles.info}>優先級: {task.priority}</Text>
<View style={styles.actionContainer}>
<TouchableOpacity style={styles.actionButton} onPress={handleEdit}>
<Text style={styles.actionButtonText}>編輯</Text>
</TouchableOpacity>
<TouchableOpacity style={[styles.actionButton, styles.deleteButton]} onPress={handleDelete}>
<Text style={styles.actionButtonText}>刪除</Text>
</TouchableOpacity>
</View>
<View style={styles.completionContainer}>
<Text style={styles.completionText}>任務完成</Text>
<Switch
value={isCompleted}
onValueChange={toggleCompletion}
/>
</View>
</View>
);
};
const styles = StyleSheet.create({
container: {
flex: 1,
padding: 10,
},
title: {
fontSize: 20,
fontWeight: 'bold',
marginBottom: 10,
},
description: {
fontSize: 16,
marginBottom: 10,
},
info: {
fontSize: 14,
marginBottom: 5,
},
actionContainer: {
flexDirection: 'row',
justifyContent: 'space-between',
marginTop: 20,
},
actionButton: {
backgroundColor: '#007AFF',
padding: 10,
borderRadius: 5,
flex: 1,
marginRight: 10,
},
deleteButton: {
backgroundColor: '#FF3B30',
marginRight: 0,
},
actionButtonText: {
color: 'white',
textAlign: 'center',
fontWeight: 'bold',
},
completionContainer: {
flexDirection: 'row',
alignItems: 'center',
justifyContent: 'space-between',
marginTop: 20,
},
completionText: {
fontSize: 16,
},
});
export default TaskDetailsPage;
src\pages\statistics-page.tsx
:
import React, { useRef, useEffect } from 'react';
import { View, Text, StyleSheet } from 'react-native';
import * as echarts from 'echarts/core';
import { SkiaChart, SVGRenderer } from '@wuba/react-native-echarts';
import { LineChart } from 'echarts/charts';
import { GridComponent } from 'echarts/components';
echarts.use([SVGRenderer, LineChart, GridComponent]);
type StatisticsPageProps = {
isDarkMode: boolean;
};
const StatisticsPage: React.FC<StatisticsPageProps> = ({ isDarkMode }) => {
const skiaRef = useRef<any>(null);
useEffect(() => {
const option = {
xAxis: {
type: 'category',
data: ['一月', '二月', '三月', '四月', '五月', '六月'],
},
yAxis: {
type: 'value',
min: 0,
max: 100,
},
series: [
{
name: '完成率',
data: [60, 70, 65, 80, 75, 90],
type: 'line',
smooth: true,
},
],
tooltip: {
trigger: 'axis',
},
legend: {
data: ['完成率'],
},
};
let chart: any;
if (skiaRef.current) {
chart = echarts.init(skiaRef.current, isDarkMode ? 'dark' : 'light', {
renderer: 'svg',
width: 350,
height: 300,
});
chart.setOption(option);
}
return () => chart?.dispose();
}, [isDarkMode]);
return (
<View style={styles.container}>
<Text style={styles.title}>統計畫面</Text>
<View style={styles.chartContainer}>
<Text style={styles.chartTitle}>任務完成率趨勢</Text>
<SkiaChart ref={skiaRef} />
</View>
</View>
);
};
const styles = StyleSheet.create({
container: {
flex: 1,
padding: 10,
},
title: {
fontSize: 18,
fontWeight: 'bold',
marginBottom: 10,
},
chartContainer: {
marginBottom: 20,
},
chartTitle: {
fontSize: 16,
fontWeight: 'bold',
marginBottom: 5,
},
});
export default StatisticsPage;
src\pages\settings.page.tsx
:
import React, { useState } from 'react';
import { View, Text, StyleSheet, Switch, TouchableOpacity, ScrollView } from 'react-native';
type SettingsPageProps = {
isDarkMode: boolean;
toggleDarkMode: () => void;
};
const SettingsPage: React.FC<SettingsPageProps> = ({ isDarkMode, toggleDarkMode }) => {
const [notifications, setNotifications] = useState(true);
const [autoSync, setAutoSync] = useState(true);
const handleLogout = () => {
// Handle logout logic
console.log('User logged out');
};
return (
<ScrollView style={styles.container}>
<Text style={styles.title}>用戶設置畫面</Text>
<View style={styles.section}>
<Text style={styles.sectionTitle}>用戶信息</Text>
<View style={styles.settingItem}>
<Text style={styles.settingLabel}>用戶名稱</Text>
<Text style={styles.settingValue}>John Doe</Text>
</View>
<View style={styles.settingItem}>
<Text style={styles.settingLabel}>電子郵件</Text>
<Text style={styles.settingValue}>johndoe@example.com</Text>
</View>
</View>
<View style={styles.section}>
<Text style={styles.sectionTitle}>應用設置</Text>
<View style={styles.settingItem}>
<Text style={styles.settingLabel}>深色模式</Text>
<Switch value={isDarkMode} onValueChange={toggleDarkMode} />
</View>
<View style={styles.settingItem}>
<Text style={styles.settingLabel}>通知</Text>
<Switch value={notifications} onValueChange={setNotifications} />
</View>
</View>
<View style={styles.section}>
<Text style={styles.sectionTitle}>同步設置</Text>
<View style={styles.settingItem}>
<Text style={styles.settingLabel}>自動同步</Text>
<Switch value={autoSync} onValueChange={setAutoSync} />
</View>
</View>
<TouchableOpacity style={styles.logoutButton} onPress={handleLogout}>
<Text style={styles.logoutButtonText}>登出</Text>
</TouchableOpacity>
<View style={styles.aboutSection}>
<Text style={styles.sectionTitle}>關於應用</Text>
<Text style={styles.aboutText}>版本: 1.0.0</Text>
<Text style={styles.aboutText}>開發者: Your Company Name</Text>
</View>
</ScrollView>
);
};
const styles = StyleSheet.create({
container: {
flex: 1,
padding: 10,
},
title: {
fontSize: 18,
fontWeight: 'bold',
marginBottom: 20,
},
section: {
marginBottom: 20,
},
sectionTitle: {
fontSize: 16,
fontWeight: 'bold',
marginBottom: 10,
},
settingItem: {
flexDirection: 'row',
justifyContent: 'space-between',
alignItems: 'center',
paddingVertical: 10,
borderBottomWidth: 1,
borderBottomColor: '#ccc',
},
settingLabel: {
fontSize: 14,
},
settingValue: {
fontSize: 14,
color: '#666',
},
logoutButton: {
backgroundColor: '#FF3B30',
padding: 10,
borderRadius: 5,
alignItems: 'center',
marginTop: 20,
},
logoutButtonText: {
color: 'white',
fontSize: 16,
fontWeight: 'bold',
},
aboutSection: {
marginTop: 20,
},
aboutText: {
fontSize: 14,
color: '#666',
marginBottom: 5,
},
});
export default SettingsPage;
這是目前暫時設計的pages,之後會一個一個修改並調整。
發現使用一些套件安裝後,總會出現一些問題。目前的作法是將模擬器的app關掉,然後整個重啟重新跑指令這樣,忽然就好了。